package com.example.demo.common.lbsgeo;

import com.example.demo.common.lbsgeo.geo.Geohash;
import com.example.demo.common.lbsgeo.geo.SerializeUtil;
import com.example.demo.common.lbsgeo.geo.po.Person;
import com.example.demo.common.lbsgeo.procon.Consumer;
import com.example.demo.common.lbsgeo.procon.Producer;
import com.example.demo.common.lbsgeo.pubsub.PrintListener;
import com.example.demo.common.lbsgeo.pubsub.PubClient;
import com.example.demo.common.lbsgeo.pubsub.SubClient;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
import redis.clients.jedis.JedisPubSub;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;

/**
 * Created by li on 2016/4/16.
 */
public class Test {
    private JedisPool jedisPool;

    public static void main(String[] args) throws Exception {
        Test test = new Test().init();

        //发布者订阅模式
//        test.pubsub();
        //消费生产模式
//        test.procon();
        //位置搜索
        //我的位置
        double[] mylocation = {36.1052, 120.3800};
//        double[] mylocation = {36.1052, 167.3800};
        //男man，女woman
        String sex = "woman";
        boolean isSort = false;
        //70公里范围内
        double range = 70 * 1000;
//        限制取的条数
        int number = 5;
        //清空redis
      //  test.clearDB();
        //生成测试数据
     //   test.createData();
        //不排序不限性别
        test.geo(mylocation, range, number);
//        test.geo(mylocation, range, sex, isSort);
    }

    /**
     * 初始化redis配置
     *
     * @return
     */
    public Test init() {
        JedisPoolConfig config = new JedisPoolConfig();
        config.setMaxIdle(300);
        config.setTestOnBorrow(true);
        this.jedisPool = new JedisPool(config, "127.0.0.1", 6379, 10000, "123456");
        return this;
    }

    /**
     * 清空redis
     */
    public void clearDB() {
        Jedis jedis = jedisPool.getResource();
        jedis.flushDB();
        returnJedis(jedis);
    }

    /**
     * 生成测试数据
     */
    public void createData() {
        Jedis jedis = jedisPool.getResource();
        try {
            int precision = 3;

            //生成位置测试数据
            double[] qingdao = {36.1052, 170.3844};
            System.out.println("生成测试数据...");
            for (int i = 0; i < 1000; i++) {
                //initLocations(jedis, qingdao[0], qingdao[1], i, precision);
                initPersons(jedis, qingdao[0], qingdao[1], (int) (System.currentTimeMillis() % 2), i, (int) (System.currentTimeMillis() % 10), precision);
//                qingdao[1] -= 0.0001;
                qingdao[1] -= 0.1;
            }
        } catch (Exception e) {

        } finally {
            returnJedis(jedis);
        }
    }

    /**
     * geo位置搜索
     *
     * @param mylocation 我的位置经纬度
     * @param range      距离范围：米
     */
    public void geo(double[] mylocation, double range, int number) {
        geo(mylocation, range, null, false, number);
    }

    /**
     * geo位置搜索
     *
     * @param mylocation 我的位置经纬度
     * @param range      距离范围。单位：米
     * @param sex        性别
     * @param isSort     是否按值排序
     */
    public void geo(double[] mylocation, double range, String sex, boolean isSort, int number) {
        Jedis jedis = jedisPool.getResource();
        try {
            //精度（5位误差10平方千米，6位误差0.34平方千米）
            int precision = 3;
            /**
             * 进行搜索
             */

            if (isSort) {
                //按值
                search(jedis, mylocation[0], mylocation[1], precision, range, true, number);
            } else {
                //按性别
                search(jedis, mylocation[0], mylocation[1], precision, range, sex, number);
            }

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            returnJedis(jedis);
        }

    }

    /**
     * 按性别
     *
     * @param jedis     jedis
     * @param lat       纬度
     * @param lon       经度
     * @param precision 截取字符串位数
     * @param range     距离范围（单位：米）
     * @param sex       性别（男：man，女：woman）
     */
    public void search(Jedis jedis, double lat, double lon, int precision, double range, String sex, int number) {
        search(jedis, lat, lon, precision, range, sex, false, number);
    }

    /**
     * 按值
     *
     * @param jedis     jedis
     * @param lat       纬度
     * @param lon       经度
     * @param precision 截取字符串位数
     * @param range     距离范围（单位：米）
     * @param sort      按值排序
     */
    public void search(Jedis jedis, double lat, double lon, int precision, double range, boolean sort, int number) {
        search(jedis, lat, lon, precision, range, null, sort, number);
    }

    /**
     * redis搜索附近的人
     *
     * @param jedis
     * @param lat
     * @param lon
     * @param precision
     * @param range
     * @param sex
     */
    public void search(Jedis jedis, double lat, double lon, int precision, double range, String sex, boolean sort, int number) {
        long start = System.currentTimeMillis();
        String geohash = Geohash.encode(lat, lon);
        String key = geohash.substring(0, precision);
        List<Person> personsList = new ArrayList<Person>();
        //搜索中心区域及周围八个区域
        String[] expand = Geohash.expand(key);
        for (String s : expand) {
            Set<byte[]> bytes = jedis.smembers(s.getBytes());
            for (byte[] aByte : bytes) {
                Person person = (Person) SerializeUtil.unserialize(aByte);
                if (person != null)
                    personsList.add(person);
            }

        }
        System.out.println("redis耗时:" + (System.currentTimeMillis() - start) + "ms");


        //性别筛选
        List<Person> filterList = new ArrayList<Person>();
        if (sex != null) {
            for (int i = 0; i < personsList.size(); i++) {
                String sexStr = personsList.get(i).getSex() == 0 ? "man" : "woman";
                if (sex.equalsIgnoreCase(sexStr))
                    filterList.add(personsList.get(i));
            }
        } else {
            filterList = personsList;
        }


        //距离排序
        Person[] persons = filterList.toArray(new Person[filterList.size()]);
        System.out.println("排序..." + persons.length);
        for (int i = 0; i < persons.length - 1; i++) {
            for (int j = 0; j < persons.length - 1 - i; j++) {
                double distance1 = 0.0;
                if (persons[j].getDistance() != 0.0) {
                    distance1 = persons[j].getDistance();
                } else {
                    distance1 = Geohash.getDistance(lat, lon, persons[j].getLat(), persons[j].getLon());
                    persons[j].setDistance(Math.round(distance1));
                }
                double distance2 = 0.0;
                if (persons[j + 1].getDistance() != 0.0) {
                    distance2 = persons[j + 1].getDistance();
                } else {
                    distance2 = Geohash.getDistance(lat, lon, persons[j + 1].getLat(), persons[j + 1].getLon());
                    persons[j + 1].setDistance(Math.round(distance2));
                }
                if (distance1 > distance2) {
                    Person temp = persons[j];
                    persons[j] = persons[j + 1];
                    persons[j + 1] = temp;
                }
            }
        }

        if (sort) {
            //优先度排序
            for (int i = 0; i < persons.length - 1; i++) {
                for (int j = 0; j < persons.length - 1 - i; j++) {
                    if (persons[j].getSort() < persons[j + 1].getSort()) {
                        Person temp = persons[j];
                        persons[j] = persons[j + 1];
                        persons[j + 1] = temp;
                    }
                }
            }
        }


        //显示结果
        for (int i = 0; i < persons.length; i++) {
            Person person = persons[i];
            if (person.getDistance() > range || (i + 1) > number) break;
            if (sex != null) {
                if (sex.equalsIgnoreCase("man")) {
                    System.out.println("男-->" + person);
                } else {
                    System.out.println("女-->" + person);
                }
            } else {
                System.out.println(person);
            }
        }

        System.out.println("搜索耗时:" + (System.currentTimeMillis() - start) + "ms");
    }


    /**
     * 生成人物位置数据
     *
     * @param jedis
     * @param lat
     * @param lon
     * @param sex
     * @param i
     * @param precision
     */
    public void initPersons(Jedis jedis, double lat, double lon, int sex, int i, int sort, int precision) {
        //经纬度geohash处理
        String geohash = Geohash.encode(lat, lon);
        //截取精度（5位误差10平方千米，6位误差0.34平方千米）
        String key = geohash.substring(0, precision);
        Person person = new Person();
        person.setId(String.valueOf(i));
        person.setLat(lat);
        person.setLon(lon);
        person.setSex(sex);
        person.setSort(sort);
        jedis.sadd(key.getBytes(), SerializeUtil.serialize(person));
    }


    /**
     * 发布者订阅者模式
     */
    public void pubsub() {
        Jedis jedisOfPub = jedisPool.getResource();
        try {
            final String channel = "test";

            PubClient pubClient = new PubClient(jedisOfPub);
            pubClient.pub(channel, "helloworld");

            Thread.sleep(500);

            Thread subThread = new Thread(new Runnable() {
                public void run() {
                    Jedis jedisOfSub = jedisPool.getResource();
                    try {
                        SubClient subClient = new SubClient(jedisOfSub);
                        System.out.println("----------subscribe operation begin-------");
                        JedisPubSub listener = new PrintListener();
                        subClient.sub(listener, channel);
                        System.out.println("----------subscribe operation end-------");
                    } catch (Exception e) {
                        e.printStackTrace();
                    } finally {
                        returnJedis(jedisOfSub);
                    }
                }
            });
            subThread.start();

            int i = 0;
            while (i < 10) {
                String message = String.valueOf(System.currentTimeMillis());
                pubClient.pub(channel, message);
                i++;
                Thread.sleep(1000);
            }
            pubClient.pub(channel, "quit");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            returnJedis(jedisOfPub);
        }

    }

    /**
     * 生产者消费者模式
     */
    public void procon() {
        Jedis jedisOfPro = jedisPool.getResource();
        try {
            final String key = "testprocon";

            Producer producer = new Producer(jedisPool.getResource());
            for (int i = 0; i < 3; i++) {
                producer.produce("testprocon", (i + 1) + "元");
                System.out.println("发布红包---" + (i + 1) + "元");
            }

            Thread.sleep(100);

            int consumerNums = 10;
            List<Thread> threadList = new ArrayList<Thread>();
            for (int i = 0; i < consumerNums; i++) {
                final int finalI = i;
                Thread consume = new Thread(new Runnable() {
                    public void run() {
                        try {
                            Jedis jedisOfCon = jedisPool.getResource();
                            Consumer consumer = new Consumer(jedisOfCon);
                            System.out.println("消费者" + (finalI + 1) + "号---取值:" + consumer.consume(key));
                            returnJedis(jedisOfCon);
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                });
                threadList.add(consume);
            }
            for (Thread thread : threadList) {
                thread.start();
            }

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            returnJedis(jedisOfPro);
        }


    }

    public void returnJedis(Jedis jedis) {
        jedisPool.returnResource(jedis);
    }


}
